/**
 * 
 */
package edu.umd.clip.lm.util;


/**
 * @author Denis Filimonov <den@cs.umd.edu>
 *
 */
public final class ProbMath {

	public final static double LOG2 = Math.log(2.0);
	public final static double INV_LOG2 = 1.0 / LOG2;
	public final static double BYTELOG2LOG10 = Math.log10(1.0001);
	public final static float MIN_FLOAT_PRECISION = (float) Math.pow(2, -21);
	
	public final static double log2(double x) {
		return INV_LOG2 * Math.log(x);
	}
	
	public final static boolean approxEqual(double d1, double d2) {
		return approxEqual(d1, d2, 0.001);
	}
	
	public final static boolean approxEqual(double d1, double d2, double precision) {
		return Math.abs(d1 - d2) <= (Math.abs(d1) + Math.abs(d2)) * 0.5 * precision;
	}
	
	public final static double bytelog2log10(int bytelog) {
		// A bytelog is a logarithm to base 1.0001, divided by 1024 and rounded to an integer.
		return 1024 * BYTELOG2LOG10 * bytelog;
	}
	
	public final static int log10toBytelog(double log10) {
		return (int) Math.round(log10 / (BYTELOG2LOG10 * 1024));
	}
	
	public final static double computeEntropy(Long2DoubleMap dist) {
		double entropy = 0;
		double totalCount = 0;
		for(Long2DoubleMap.Iterator i = dist.iterator(); i.hasNext();) {
			double prob = i.next().getValue();
			totalCount += prob;
			entropy -= prob * Math.log(prob);
		}
		entropy /= totalCount;
		entropy += Math.log(totalCount);
		// convert to bits
		entropy *= INV_LOG2;
		return entropy;
	}

	public final static double computeEntropy(Long2IntMap dist) {
		double entropy = 0;
		long totalCount = 0;
		for(Long2IntMap.Iterator i = dist.iterator(); i.hasNext();) {
			int count = i.next().getValue();
			totalCount += count;
			entropy -= count * Math.log(count);
		}
		entropy /= totalCount;
		entropy += Math.log(totalCount);
		// convert to bits
		entropy *= INV_LOG2;
		return entropy;
	}

	public final static double computeEntropy(Long2LongMap dist) {
		double entropy = 0;
		long totalCount = 0;
		for(Long2LongMap.Iterator i = dist.iterator(); i.hasNext();) {
			long count = i.next().getValue();
			totalCount += count;
			entropy -= count * Math.log(count);
		}
		entropy /= totalCount;
		entropy += Math.log(totalCount);
		// convert to bits
		entropy *= INV_LOG2;
		return entropy;
	}

	public final static double computeEntropy(ConstCountDistribution dist) {
		double entropy = 0;
		for(long count : dist.values()) {
			entropy -= count * Math.log(count);
		}
		entropy /= dist.getTotalCount();
		entropy += Math.log(dist.getTotalCount());
		// convert to bits
		entropy *= INV_LOG2;
		return entropy;
	}
}
